package com.autumn.mybatis.provider;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.map.CaseInsensitiveMap;

import com.autumn.mybatis.provider.annotation.ProviderDrive;
import com.autumn.mybatis.provider.mysql.MySqlProvider;
import com.autumn.util.ExceptionUtils;
import com.autumn.util.PackageUtils;
import com.autumn.util.StringUtils;

/**
 * 驱动帮助
 * 
 * @author 老码农
 *         <p>
 *         Description
 *         </p>
 * @date 2017-11-04 02:56:23
 */
public class ProviderUtils {

	@SuppressWarnings("unchecked")
	private static final Map<String, Class<? extends IDbProvider>> DB_PROVIDER_MAP = new CaseInsensitiveMap();	
	
	static {
		registerDbProviderClassByPackage(ProviderUtils.class.getPackage().getName());
	}

	/**
	 * 根据类型、或提供者名称搜索提供者类型，
	 * 
	 * @param driveType
	 *            提供者类型
	 * @param driveName
	 *            提供者名称
	 * @return 若driveClassName与driveName 均为 null 或者空白值则返回 MySqlProvider
	 */
	public static Class<? extends IDbProvider> getOrDefaultDbProviderClass(Class<? extends IDbProvider> driveType,
			String driveName) {
		Class<? extends IDbProvider> providerType = null;
		if (!StringUtils.isNullOrBlank(driveName)) {
			providerType = ProviderUtils.getDbProviderClass(driveName);
			if (providerType == null) {
				throw ExceptionUtils.throwConfigureException(driveName + " 不是内置驱动提供者。");
			}
			return providerType;
		}
		if (driveType != null) {
			return driveType;
		}
		return MySqlProvider.class;
	}

	/**
	 * 根据类名、或提供者名称搜索提供者类型，
	 * 
	 * @param driveClassName
	 *            提供者类名称
	 * @param driveName
	 *            提供者名称
	 * @return 若driveClassName与driveName 均为 null 或者空白值则返回 MySqlProvider
	 */
	public static Class<?> getOrDefaultDbProviderClass(String driveClassName, String driveName) {
		Class<?> providerType = null;
		if (!StringUtils.isNullOrBlank(driveClassName)) {
			try {
				providerType = Class.forName(driveClassName);
			} catch (ClassNotFoundException e) {
				throw ExceptionUtils.throwConfigureException(e.getMessage(), e);
			}
		}
		if (providerType == null) {
			if (!StringUtils.isNullOrBlank(driveName)) {
				providerType = ProviderUtils.getDbProviderClass(driveName);
				if (providerType == null) {
					throw ExceptionUtils.throwConfigureException(driveName + " 不是内置驱动提供者。");
				}
			}
		}
		if (providerType != null) {
			if (!IDbProvider.class.isAssignableFrom(providerType)) {
				throw ExceptionUtils.throwConfigureException(providerType + " 不是 " + IDbProvider.class + " 的实现。");
			}
			return providerType;
		}
		return MySqlProvider.class;
	}

	/**
	 * 获取驱动类型
	 * 
	 * @param driveName
	 *            驱动名称(不区分大小写)
	 * @return
	 */
	public static Class<? extends IDbProvider> getDbProviderClass(String driveName) {
		Class<? extends IDbProvider> providerType = DB_PROVIDER_MAP
				.get(ExceptionUtils.checkNotNullOrBlank(driveName, "driveName"));
		return providerType;
	}

	/**
	 * 基于驱动名称创建数据驱动
	 * 
	 * @param driveName
	 *            驱动名称
	 * @return
	 */
	public static IDbProvider createDbProviderForDriveName(String driveName) {
		Class<?> providerType = getDbProviderClass(driveName);
		if (providerType == null) {
			ExceptionUtils.throwAutumnException("驱动 " + driveName + " 无对应的实现类型。");
		}
		try {
			return (IDbProvider) providerType.newInstance();
		} catch (InstantiationException e) {
			throw ExceptionUtils.throwUserFriendlyException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			throw ExceptionUtils.throwUserFriendlyException(e.getMessage(), e);
		}
	}

	/**
	 * 基于类型名称创建数据驱动
	 * 
	 * @param driveName
	 *            驱动名称
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static IDbProvider createDbProviderForClassName(String className) throws ClassNotFoundException {
		ExceptionUtils.checkNotNullOrBlank(className, "className");
		Class<?> providerType = Class.forName(className);
		try {
			return (IDbProvider) providerType.newInstance();
		} catch (InstantiationException e) {
			throw ExceptionUtils.throwUserFriendlyException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			throw ExceptionUtils.throwUserFriendlyException(e.getMessage(), e);
		}
	}

	/**
	 * 注册驱动类型
	 * 
	 * @param driveName
	 *            驱动名称
	 * @param providerType
	 *            驱动类型
	 */
	public static void registerDbProviderClass(Class<? extends IDbProvider> providerType) {
		ExceptionUtils.checkNotNull(providerType, "providerType");
		ProviderDrive drive = providerType.getAnnotation(ProviderDrive.class);
		if (drive == null || StringUtils.isNullOrBlank(drive.name())) {
			ExceptionUtils.throwAutumnException(providerType + " 未配置 " + ProviderDrive.class + " 的注解或注解名称为空白值。");
		}
		if (providerType.isInterface() || Modifier.isAbstract(providerType.getModifiers())) {
			ExceptionUtils.throwAutumnException(providerType + " 不是实现类型。");
		}
		registerDbProviderClass(drive.name(), providerType);
	}

	/**
	 * 注册驱动类型
	 * 
	 * @param driveName
	 *            驱动名称
	 * @param providerType
	 *            驱动类型
	 */
	public static void registerDbProviderClass(String driveName, Class<? extends IDbProvider> providerType) {
		ExceptionUtils.checkNotNullOrBlank(driveName, "driveName");
		ExceptionUtils.checkNotNull(providerType, "providerType");
		if (providerType.isInterface() || Modifier.isAbstract(providerType.getModifiers())) {
			ExceptionUtils.throwAutumnException(providerType + " 不是实现类型。");
		}
		DB_PROVIDER_MAP.put(driveName, providerType);
	}

	/**
	 * 按包注册Db驱动类型
	 * 
	 * @param packageName
	 *            包名称
	 */
	@SuppressWarnings("unchecked")
	public static void registerDbProviderClassByPackage(String packageName) {
		try {
			Set<Class<?>> providerTypeSet = PackageUtils.getPackageTargetClass(
					ExceptionUtils.checkNotNullOrBlank(packageName, "packageName"), IDbProvider.class, false, false,
					true);
			for (Class<?> providerType : providerTypeSet) {
				ProviderDrive drive = providerType.getAnnotation(ProviderDrive.class);
				if (drive != null && !StringUtils.isNullOrBlank(drive.name())) {
					DB_PROVIDER_MAP.put(drive.name(), (Class<? extends IDbProvider>) providerType);
				}
			}

		} catch (IOException e) {
			ExceptionUtils.throwSystemException(e.getMessage(), e);
		}
	}
}
